
#include <BFont>
#include <BPainter>
#include <BMouseEvent>

#include <BMenu>
#include "member_BMenu.h"
#include "member_BAction.h"
#include "member_BLayout.h"
#include "member_BStyle.h"

using namespace BWE;

#define member					(*(member_BMenu*)_ptr)
#define member_allocate()		_ptr = new member_BMenu(this)
#define member_release()		delete (member_BMenu*)_ptr

BMenu::BMenu(BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->hide();
}
BMenu::BMenu(const BString& title, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setTitle(title);
	this->hide();
}
BMenu::BMenu(BIcon* icon, const BString& title, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setTitle(title);
	this->setIcon(icon);
	this->hide();
}
BMenu::~BMenu()
{
	for (int i = 0; i < member.items.size(); i++)
	{
		IMenuItem& item = member.items[i];
		if (item.action)
			member.disconnect(item.action);
		if (item.menu)
			member.disconnect(item.menu);
	}
	member_release();
}

void BMenu::setAlign(Align align)
{
	if (member.align != align)
	{
		member.align = align;
		member.dirty = true;
		this->fresh();
	}
}
Align BMenu::align() const
{
	return member.align;
}

void BMenu::setForm(Form form)
{
	if (member.form != form)
	{
		member.form = form;
		member.dirty = true;
		this->fresh();
	}
}
Form BMenu::form() const
{
	return member.form;
}

void BMenu::setSpacing(int spacing)
{
	if (member.spacing != spacing)
	{
		member.spacing = spacing;
		member.dirty = true;
		this->fresh();
	}
}
int BMenu::spacing() const
{
	return member.spacing;
}

void BMenu::setOrientation(Orientation orientation)
{
	if (member.orientation != orientation)
	{
		member.orientation = orientation;
		member.dirty = true;
		this->emit(Signal_OrientationChanged, orientation);
		this->fresh();
	}
}
Orientation BMenu::orientation() const
{
	return member.orientation;
}

void BMenu::setShortcutVisible(bool shortcutVisible)
{
	if (member.shortcutVisible != shortcutVisible)
	{
		member.shortcutVisible = shortcutVisible;
		member.dirty = true;
		this->fresh();
	}
}
bool BMenu::shortcutVisible() const
{
	return member.shortcutVisible;
}

void BMenu::setOffset(int offx, int offy)
{
	setOffset(BPoint(offx, offy));
}
void BMenu::setOffset(const BPoint& offset)
{
	if (member.offset != offset)
	{
		member.offset = offset;
		this->emit(Signal_OffsetChanged, member.offset);
	}
}
const BPoint& BMenu::offset() const
{
	return member.offset;
}

void BMenu::setIconSize(const BSize& iconSize)
{
	if (member.iconSize != iconSize)
	{
		member.iconSize = iconSize;
		member.dirty = true;
		this->fresh();
	}
}
const BSize& BMenu::iconSize() const
{
	return member.iconSize;
}

bool BMenu::addAction(BAction* action)
{
	if (!action)
		return false;
	if (member.items.contain(action))
		return true;
	if (member.items.append(action))
	{
		member.connect(action, Signal_Renamed, &member_BMenu::slotActionRenamed);
		member.connect(action, Signal_Enabled, &member_BMenu::slotActionEnabled);
		member.connect(action, Signal_IconChanged, &member_BMenu::slotActionIconChanged);
		member.connect(action, Signal_TextChanged, &member_BMenu::slotActionTextChanged);
		member.connect(action, Signal_Checked, &member_BMenu::slotActionChecked);
		member.connect(action, Signal_Triggered, &member_BMenu::slotActionTriggered);
		member.connect(action, Signal_Show, &member_BMenu::slotActionVisibleChanged);
		member.connect(action, Signal_Hide, &member_BMenu::slotActionVisibleChanged);
		member.dirty = true;
		this->emit(Signal_Insert, BValue(action, this));
		this->fresh();
		return true;
	}
	return false;
}
BAction* BMenu::addAction(const BString& text)
{
	BAction* action = new BAction(text);
	addAction(action);
	return action;
}
BAction* BMenu::addAction(BIcon* icon, const BString& text)
{
	BAction* action = new BAction(icon, text);
	addAction(action);
	return action;
}
BAction* BMenu::addAction(BImage* image, const BString& text)
{
	BAction* action = new BAction(image, text);
	addAction(action);
	return action;
}

bool BMenu::insertAction(int pos, BAction* action)
{
	if (!action)
		return false;
	if (member.items.contain(action))
	{
		int index = member.items.find(action);
		member.items.move(index, pos);
		return true;
	}
	if (member.items.insert(pos, action))
	{
		member.connect(action, Signal_Renamed, &member_BMenu::slotActionRenamed);
		member.connect(action, Signal_Enabled, &member_BMenu::slotActionEnabled);
		member.connect(action, Signal_IconChanged, &member_BMenu::slotActionIconChanged);
		member.connect(action, Signal_TextChanged, &member_BMenu::slotActionTextChanged);
		member.connect(action, Signal_Checked, &member_BMenu::slotActionChecked);
		member.connect(action, Signal_Triggered, &member_BMenu::slotActionTriggered);
		member.connect(action, Signal_Show, &member_BMenu::slotActionVisibleChanged);
		member.connect(action, Signal_Hide, &member_BMenu::slotActionVisibleChanged);
		member.dirty = true;
		this->emit(Signal_Insert, BValue(action, this));
		this->fresh();
		return true;
	}
	return false;
}
BAction* BMenu::insertAction(int pos, const BString& text)
{
	BAction* action = new BAction(text);
	insertAction(pos, action);
	return action;
}
BAction* BMenu::insertAction(int pos, BIcon* icon, const BString& text)
{
	BAction* action = new BAction(icon, text);
	insertAction(pos, action);
	return action;
}
BAction* BMenu::insertAction(int pos, BImage* image, const BString& text)
{
	BAction* action = new BAction(image, text);
	insertAction(pos, action);
	return action;
}

bool BMenu::removeAction(BAction* action)
{
	if (!action)
		return false;
	if (member.items.contain(action))
	{
		member.disconnect(action);
		member.items.remove(action);
		member.dirty = true;
		this->adjust();
		this->fresh();
		this->emit(Signal_Remove, BValue(action, this));
		return true;
	}
	return false;
}
bool BMenu::removeAction(const BString& actionText)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BAction* action = member.items[i].action;
		if (action == 0)
			continue;
		if (action->text() == actionText)
		{
			member.disconnect(action);
			member.items.remove(i);
			member.dirty = true;
			this->fresh();
			return true;
		}
	}
	return false;
}

BAction* BMenu::action(int index)
{
	return member.items[index].action;
}
const BAction* BMenu::action(int index) const
{
	return member.items[index].action;
}

BAction* BMenu::action(const BString& text)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BAction* action = member.items[i].action;
		if (action && action->text() == text)
			return action;
	}
	return 0;
}
const BAction* BMenu::action(const BString& text) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		const BAction* action = member.items[i].action;
		if (action && action->text() == text)
			return action;
	}
	return 0;
}

BAction* BMenu::actionOfAsset(const BValue& value)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BAction* action = member.items[i].action;
		if (action && action->asset() == value)
			return action;
	}
	return 0;
}
const BAction* BMenu::actionOfAsset(const BValue& value) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		const BAction* action = member.items[i].action;
		if (action && action->asset() == value)
			return action;
	}
	return 0;
}

BAction* BMenu::namedAction(const BString& name)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BAction* action = member.items[i].action;
		if (action && action->name() == name)
			return action;
	}
	return 0;
}
const BAction* BMenu::namedAction(const BString& name) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BAction* action = member.items[i].action;
		if (action && action->name() == name)
			return action;
	}
	return 0;
}

BAction* BMenu::findAction(const BString& text)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		IMenuItem& item = member.items[i];
		if (item.action && item.action->text() == text)
			return item.action;
		if (!item.menu)
			continue;
		BAction* action = item.menu->findAction(text);
		if (action)
			return action;
	}
	return 0;
}
const BAction* BMenu::findAction(const BString& text) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		const IMenuItem& item = member.items[i];
		if (item.action && item.action->text() == text)
			return item.action;
		if (!item.menu)
			continue;
		const BAction* action = item.menu->findAction(text);
		if (action)
			return action;
	}
	return 0;
}

BAction* BMenu::findActionOfAsset(const BValue& value)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		IMenuItem& item = member.items[i];
		if (item.action && item.action->asset() == value)
			return item.action;
		if (!item.menu)
			continue;
		BAction* action = item.menu->findActionOfAsset(value);
		if (action)
			return action;
	}
	return 0;
}
const BAction* BMenu::findActionOfAsset(const BValue& value) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		IMenuItem& item = member.items[i];
		if (item.action && item.action->asset() == value)
			return item.action;
		if (!item.menu)
			continue;
		const BAction* action = item.menu->findActionOfAsset(value);
		if (action)
			return action;
	}
	return 0;
}

BAction* BMenu::findNamedAction(const BString& name)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		IMenuItem& item = member.items[i];
		if (item.action && item.action->name() == name)
			return item.action;
		if (!item.menu)
			continue;
		BAction* action = item.menu->findNamedAction(name);
		if (action)
			return action;
	}
	return 0;
}
const BAction* BMenu::findNamedAction(const BString& name) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		const IMenuItem& item = member.items[i];
		if (item.action && item.action->name() == name)
			return item.action;
		if (!item.menu)
			continue;
		const BAction* action = item.menu->findNamedAction(name);
		if (action)
			return action;
	}
	return 0;
}

BAction* BMenu::locateAction(const BString& path)
{
	if (path.empty())
		return 0;
	if (path.contain('/'))
	{
		BString title;
		BMenu* menu = member.locateMenu(path, title);
		return menu->action(title);
	}
	return findAction(path);
}
const BAction* BMenu::locateAction(const BString& path) const
{
	if (path.empty())
		return 0;
	if (path.contain('/'))
	{
		BString title;
		const BMenu* menu = member.locateMenu(path, title);
		return menu->action(title);
	}
	return findAction(path);
}

bool BMenu::addMenu(BMenu* menu)
{
	if (!menu)
		return false;
	if (menu == this)
		return false;
	if (member.items.contain(menu))
		return true;
	if (member.items.append(menu))
	{
		member.connect(menu, Signal_Enabled, &member_BMenu::slotMenuEnabled);
		member.connect(menu, Signal_Renamed, &member_BMenu::slotMenuRenamed);
		member.connect(menu, Signal_Styled, &member_BMenu::slotMenuStyled);
		member.connect(menu, Signal_IconChanged, &member_BMenu::slotMenuIconChanged);
		member.connect(menu, Signal_TitleChanged, &member_BMenu::slotMenuTitleChanged);
		member.connect(menu, Signal_Show, &member_BMenu::slotMenuVisibleChanged);
		member.connect(menu, Signal_Hide, &member_BMenu::slotMenuVisibleChanged);
		this->addChild(menu);
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
BMenu* BMenu::addMenu(const BString& title)
{
	BMenu* menu = new BMenu(title);
	addMenu(menu);
	return menu;
}
BMenu* BMenu::addMenu(BIcon* icon, const BString& title)
{
	BMenu* menu = new BMenu(icon, title);
	addMenu(menu);
	return menu;
}

bool BMenu::insertMenu(int pos, BMenu* menu)
{
	if (!menu)
		return false;
	if (member.items.contain(menu))
	{
		int index = member.items.find(menu);
		member.items.move(index, pos);
		return true;
	}
	if (member.items.insert(pos, menu))
	{
		member.connect(menu, Signal_Enabled, &member_BMenu::slotMenuEnabled);
		member.connect(menu, Signal_Renamed, &member_BMenu::slotMenuRenamed);
		member.connect(menu, Signal_Styled, &member_BMenu::slotMenuStyled);
		member.connect(menu, Signal_IconChanged, &member_BMenu::slotMenuTitleChanged);
		member.connect(menu, Signal_TitleChanged, &member_BMenu::slotMenuTitleChanged);
		member.connect(menu, Signal_Show, &member_BMenu::slotMenuVisibleChanged);
		member.connect(menu, Signal_Hide, &member_BMenu::slotMenuVisibleChanged);
		this->addChild(menu);
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
BMenu* BMenu::insertMenu(int pos, const BString& title)
{
	BMenu* menu = new BMenu(title);
	insertMenu(pos, menu);
	return menu;
}
BMenu* BMenu::insertMenu(int pos, BIcon* icon, const BString& title)
{
	BMenu* menu = new BMenu(icon, title);
	insertMenu(pos, menu);
	return menu;
}

bool BMenu::removeMenu(BMenu* menu)
{
	if (!menu)
		return false;
	if (member.items.contain(menu))
	{
		this->removeChild(menu);
		member.disconnect(menu);
		member.items.remove(menu);
		member.dirty = true;
		this->adjust();
		this->fresh();
		return true;
	}
	return false;
}
bool BMenu::removeMenu(const BString& title)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BMenu* menu = member.items[i].menu;
		if (menu && menu->title() != title)
		{
			return this->removeMenu(menu);
		}
	}
	return false;
}

BMenu* BMenu::menu(int index)
{
	return member.items(index).menu;
}
const BMenu* BMenu::menu(int index) const
{
	return member.items(index).menu;
}

BMenu* BMenu::menu(const BString& title)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BMenu* menu = member.items[i].menu;
		if (menu && menu->title() == title)
			return menu;
	}
	return 0;
}
const BMenu* BMenu::menu(const BString& title) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		const BMenu* menu = member.items[i].menu;
		if (menu && menu->title() == title)
			return menu;
	}
	return 0;
}

BMenu* BMenu::menuOfAsset(const BValue& value)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BMenu* menu = member.items[i].menu;
		if (menu && menu->asset() == value)
			return menu;
	}
	return 0;
}
const BMenu* BMenu::menuOfAsset(const BValue& value) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		const BMenu* menu = member.items[i].menu;
		if (menu && menu->asset() == value)
			return menu;
	}
	return 0;
}

BMenu* BMenu::findMenu(const BString& title)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		IMenuItem& item = member.items[i];
		if (item.menu && item.menu->title() == title)
			return item.menu;
		if (!item.menu)
			continue;
		BMenu* menu = item.menu->findMenu(title);
		if (menu)
			return menu;
	}
	return 0;
}
const BMenu* BMenu::findMenu(const BString& title) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		IMenuItem& item = member.items[i];
		if (item.menu && item.menu->title() == title)
			return item.menu;
		if (!item.menu)
			continue;
		BMenu* menu = item.menu->findMenu(title);
		if (menu)
			return menu;
	}
	return 0;
}

BMenu* BMenu::locateMenu(const BString& path)
{
	if (path.empty())
		return 0;
	if (path.contain('/'))
	{
		BString title;
		BMenu* menu = member.locateMenu(path, title);
		return menu->menu(title);
	}
	return findMenu(path);
}
const BMenu* BMenu::locateMenu(const BString& path) const
{
	if (path.empty())
		return 0;
	if (path.contain('/'))
	{
		BString title;
		const BMenu* menu = member.locateMenu(path, title);
		return menu->menu(title);
	}
	return findMenu(path);
}

bool BMenu::addSeparator()
{
	if (member.items.append(IMenuItem()))
	{
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
bool BMenu::insertSeparator(int index)
{
	if (0 <= index && index <= member.items.size())
	{
		member.items.insert(index);
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
bool BMenu::insertSeparator(const BObject* before)
{
	if (!before)
		return false;
	int index = member.items.find(before);
	if (index < member.items.size())
	{
		member.items.insert(index);
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}

bool BMenu::isSeparator(int index)
{
	if (member.items.check(index))
	{
		IMenuItem& item = member.items[index];
		return item.empty();
	}
	return false;
}
bool BMenu::removeSeparator(int index)
{
	if (member.items.check(index))
	{
		IMenuItem& item = member.items[index];
		if (item.empty())
		{
			member.items.remove(index);
			member.dirty = true;
			this->adjust();
			this->fresh();
			return true;
		}
	}
	return false;
}

void BMenu::clear()
{
	if (member.items.size())
	{
		while (member.items.size())
		{
			IMenuItem& item = member.items.last();
			if (item.action)
				member.disconnect(item.action);
			if (item.menu)
				member.disconnect(item.menu);
			member.items.remove(member.items.size() - 1);
		}
		member.dirty = true;
		this->adjust();
		this->fresh();
	}
}
int BMenu::count() const
{
	return member.items.size();
}
BRect BMenu::itemRect(int index) const
{
	return member.items(index).rect();
}
int BMenu::index(const BObject* object) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		if (member.items[i] == object)
			return i;
	}
	return -1;
}

void BMenu::finish(const BValue& value)
{
	BMenu* menu = this;
	bool popuped = true;
	while (menu)
	{
		if (popuped)
			popuped = menu->popuped();
		if (popuped)
			menu->hide();
		menu->emit(Signal_Finished, value);
		menu = dynamic_cast<BMenu*>(menu->parent());
	}
}

const BString& BMenu::realTips() const
{
	if (member.hoverIndex >= 0)
	{
		IMenuItem& item = member.items.at(member.hoverIndex);
		if (BAction* action = item.action)
		{
			if (action_member(action)->tips.size())
				return action_member(action)->tips;
		}
		if (item.menu)
		{
			const BString& tips = item.menu->realTips();
			if (tips.size())
				return tips;
		}
		return item.tips;
	}
	return BWidget::realTips();
}

BSize BMenu::sizeHint() const
{
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	return this->preferSize(member.contentSize);
}

void BMenu::popupEvent(const BEvent& event)
{
	BWidget::popupEvent(event);
	if (event.value())
	{
		BSize hsize = this->sizeHint();
		this->setSize(hsize);
		BWidget* root = this->root();
		if (root != this)
		{
			BRect rootRect = root->size();
			BRect absRect = this->absoluteRect();
			BRect newRect = absRect;
			if (newRect.left() < rootRect.left())
			{
				newRect.right() = rootRect.left() + newRect.width();
				newRect.left() = rootRect.left();
			}
			if (newRect.top() < rootRect.top())
			{
				newRect.bottom() = rootRect.top() + newRect.height();
				newRect.top() = rootRect.top();
			}
			if (newRect.right() > rootRect.right())
			{
				newRect.left() = rootRect.right() - newRect.width();
				newRect.right() = rootRect.right();
			}
			if (newRect.bottom() > rootRect.bottom())
			{
				newRect.top() = rootRect.bottom() - newRect.height();
				newRect.bottom() = rootRect.bottom();
			}
			if (newRect != absRect)
			{
				this->setAbsoluteRect(newRect);
			}
		}
	}
}

void BMenu::activeEvent(const BEvent& event)
{
	BWidget::activeEvent(event);
	if (this->actived() == false)
	{
		if (this->popuped())
			this->hide();
	}
}
void BMenu::resizeEvent(const BEvent& event)
{
	BWidget::resizeEvent(event);
	member.dirty = true;
	this->fresh();
}
void BMenu::adjustEvent(const BEvent& event)
{
	BWidget::adjustEvent(event);
	if (this->popuped())
	{
		BSize hsize = this->sizeHint();
		this->setSize(hsize);
	}
}

void BMenu::styleEvent(const BEvent& event)
{
	BWidget::styleEvent(event);
	const BStyle* style = event.value();
	member.separatorImage = 0;
	member.shortcutStyle = 0;

	if (BIcon* icon = style->icon(Icon_Arrow_Right))
		member.arrowIcon = icon;

	style_member(style)->assign(Value_Offset, member.offset);
	style_member(style)->assign(Value_Align, member.align);
	style_member(style)->assign(Value_Form, member.form);
	style_member(style)->assign(Value_Spacing, member.spacing);
	style_member(style)->assign(Value_Icon_Size, member.iconSize);
	style_member(style)->assign(Value_Arrow_Size, member.arrowSize);
	style_member(style)->assign(Value_Arrow_Graph, member.arrowGraph);
	style_member(style)->assign(Value_Arrow_Align, member.arrowAlign);
	style_member(style)->assign(Value_Orientation, member.orientation);

	if (const BStyle* separatorStyle = style->annex("separator"))
	{
		member.separatorStyle = separatorStyle;
		style_member(separatorStyle)->assign(Value_Width, member.separatorSize.width());
		style_member(separatorStyle)->assign(Value_Height, member.separatorSize.height());
		if (BImage* image = separatorStyle->value(Value_Image))
			member.separatorImage = image;
	}

	if (const BStyle* shortcutStyle = style->annex("shortcut"))
	{
		member.shortcutStyle = shortcutStyle;
		style_member(shortcutStyle)->assign(Value_Visible, member.shortcutVisible);
		style_member(shortcutStyle)->assign(Value_Spacing, member.shortcutSpacing);
	}

	for (int i = 0; i < member.items.size(); i++)
	{
		if (member.items[i].empty())
			continue;
		member.items[i].dirtyStyle = true;
		member.items[i].dirty = true;
	}
	member.dirty = true;
}
void BMenu::freshEvent(const BEvent& event)
{
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	BWidget::freshEvent(event);
}
void BMenu::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	painter.drawForeground();

	State state = painter.state();
	for (int i = 0; i < member.items.size(); i++)
	{
		IMenuItem& item = member.items[i];
		if (item.action && !item.action->visible())
			continue;
		BRect rect = item.rect();
		if (item.empty())
		{
			if (item.ia_textRect.empty())
			{
				if (member.separatorImage)
				{
					painter.drawImage(item.rect(), member.separatorImage);
				}
				else if (painter.setColor(Color_Separator))
				{
					painter.setLineWidth((float)member.separatorSize.height());
					painter.drawLine(rect.leftCenter(), rect.rightCenter());
				}
				continue;
			}
			if (member.separatorImage)
			{
				painter.drawImage(rect, member.separatorImage);
			}
			else if (painter.setColor(Color_Separator))
			{
				BPoint p0 = item.ia_textRect.leftCenter() - BPoint(member.spacing, 0);
				BPoint p1 = item.ia_textRect.rightCenter() + BPoint(member.spacing, 0);
				painter.setLineWidth((float)member.separatorSize.width());
				painter.drawLine(rect.leftCenter(), p0);
				painter.drawLine(rect.rightCenter(), p1);
			}
			item.drawContent(painter, state);
		}

		State itemState = state;
		if (!item.enabled())
			itemState = State_Disable;
		else if (item.action && item.action->checked())
			itemState = State_Checked;
		else if (item.menu && item.menu->visible())
			itemState |= State_Hovered;

		if (member.hoverIndex == i)
		{
			if (member.pressed)
				itemState |= State_Pressed;
			else
				itemState |= State_Hovered;
		}

		painter.setStyle(item.realStyle);
		painter.pushMatrix();
		painter.translate(item.pos);
		//painter.setClip(item_member(item)->rect() & crect);
		item.drawBackground(painter, itemState);
		item.drawMargin(painter, itemState);
		item.drawBorder(painter, itemState);
		item.drawContent(painter, itemState);
		if (item.action && item.shortcutRect.valid())
		{
			painter.setStyle(member.shortcutStyle);
			const BString& text = item.action->shortcut()->text();
			painter.setColor(Color_Text, itemState);
			painter.drawText(item.shortcutRect, text);
		}
		painter.popMatrix();
		painter.setStyle(0);
		if (item.menu)
		{
			if (member.arrowIcon)
			{
				painter.drawImage(item.arrowRect, member.arrowIcon->image(itemState));
			}
			else if (painter.setColor(Color_Arrow, itemState))
			{
				painter.drawGraph(item.arrowRect % 36, member.arrowGraph);
			}
		}
	}

	painter.drawPerch();
	painter.drawMargin();
	painter.drawBorder();
}

void BMenu::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverIndex != -1)
	{
		member.hoverIndex = -1;
		member.pressed = false;
		this->fresh();
	}
	BWidget::mouseEnterEvent(mouseEvent);
}
void BMenu::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverIndex != -1)
	{
		member.hoverIndex = -1;
		member.pressed = false;
		this->fresh();
	}
	BWidget::mouseLeaveEvent(mouseEvent);
}

void BMenu::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		if (member.hoverIndex >= 0)
		{
			member.pressed = true;
			this->fresh();
		}
	}
	BWidget::mousePressEvent(mouseEvent);
}
void BMenu::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		if (member.pressed)
		{
			member.pressed = false;
			this->fresh();
		}
	}
	BWidget::mouseReleaseEvent(mouseEvent);
}
void BMenu::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	int hoverIndex = -1;
	BPoint pos = mouseEvent.pos();
	for (int i = 0; i < member.items.size(); i++)
	{
		IMenuItem& item = member.items[i];
		if (item.action && !item.action->visible())
			continue;
		if (item.action && !item.action->enabled())
			continue;
		BRect actRect = item.rect();
		if (actRect.contain(pos))
		{
			hoverIndex = i;
			break;
		}
	}
	if (member.hoverIndex != hoverIndex)
	{
		member.hoverIndex = hoverIndex;
		for (int i = 0; i < member.items.size(); i++)
		{
			IMenuItem& item = member.items[i];
			if (item.menu && item.menu->enabled())
			{
				if (i == member.hoverIndex)
				{
					if (member.expanded)
					{
						item.menu->setPos(item.rect().rightTop());
						item.menu->setParent(this);
						item.menu->popup();
						item.menu->raise();
					}
				}
				else
				{
					if (item.menu->popuped())
						item.menu->hide();
				}
			}
		}
		IMenuItem& item = member.items(hoverIndex);
		if (item.action)
		{
			this->emit(Signal_ActionHovered, item.action);
		}
		this->fresh();
	}
	BWidget::mouseMoveEvent(mouseEvent);
}

void BMenu::mouseClickEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		if (member.items.check(member.hoverIndex))
		{
			IMenuItem& item = member.items[member.hoverIndex];
			if (BMenu* menu = item.menu)
			{
				member.expanded = !member.expanded;
				if (member.expanded)
				{
					menu->setPos(item.rect().rightTop());
					menu->setParent(this);
					menu->popup();
					menu->raise();
				}
				else
				{
					if (menu->popuped())
						menu->hide();
				}
			}
			BAction* action = item.action;
			if (action && action->enabled())
			{
				action->trigger();
				member.raiseSignal(Signal_ActionClicked, action);
			}
		}
	}
	BWidget::mouseClickEvent(mouseEvent);
}
